#include "resizepadcvtcolor.h"

__device__ void affine_project_device_kernel(tools::AffineMatrix* dst2src,int dst_x,int dst_y, float* cal_src_x,float* cal_src_y)
{
    *cal_src_x = dst2src->v0 * dst_x + dst2src->v1*dst_y + dst2src->v2;
    *cal_src_y = dst2src->v3*dst_x + dst2src->v4*dst_y + dst2src->v5; 
}

__global__  void resize_padding_device_kernel(unsigned char* input_src_device,int src_volume,int src_w,int src_h,float* resize_dst,int dst_volume,
        int dst_area,int dst_w,int dst_h,tools::AffineMatrix dst2src,int batch_size,float pad_value)
{
    int dx = blockDim.x * blockIdx.x + threadIdx.x;
    int dy = blockDim.y * blockIdx.y + threadIdx.y;
    if (dx < dst_area && dy<batch_size)
    {
        int dst_y = dx / dst_w;
        int dst_x = dx % dst_w;
        float cal_src_x = 0;
        float cal_src_y = 0;
        // 根据dst的（x,y）结合 affinemat--> 获取 src的 （x0,y0）
        affine_project_device_kernel(&dst2src,dst_x,dst_y,&cal_src_x,&cal_src_y);
        // 计算每一个dst（x,y）位置的三通道像素值
        float c0 = pad_value;
        float c1 = pad_value;
        float c2 = pad_value;
        // 如果索引越界了就填充默认值
        if(cal_src_x<-1 || cal_src_x>=src_w || cal_src_y<-1 || cal_src_y>=src_h)
        {
        }
        else
        {
            /*
            cal_src_x: [0,src_x-1]
            x_low-->[-1,src_x-1]
            x_high-->[1,src_x]

            cal_src_y: [0,src_y-1]
            y_low-->[-1,src_y-1]
            y_high-->[1,src_y]
            
            */
            int x_low = floor(cal_src_x);
            int y_low = floor(cal_src_y);
            int x_high = x_low + 1;
            int y_high = y_low + 1;
            /*
                x_low,y_low
                            src_x,src_y
                                        x_high,y_high
            */
            unsigned char const_values[] = {(unsigned char)pad_value,(unsigned char)pad_value,(unsigned char)pad_value};
            float lx = cal_src_x - x_low;
            float ly = cal_src_y - y_low;
            float hx = x_high - cal_src_x;
            float hy = y_high - cal_src_y;
            /*
                w4  w3
                w2  w1

                v1  v2
                v3  v4 
            */
            float w1 = hy*hx;
            float w2 = hy*lx;
            float w3 = ly*hx;
            float w4 = ly*lx;
            unsigned char* v1 = const_values;
            unsigned char* v2 = const_values;
            unsigned char* v3 = const_values;
            unsigned char* v4 = const_values;

            //双线性插值索引越界就用默认常量值。constrain之后 y_low->[0,src_y-1]
            // v1 v2通过y_low绑定
            if (y_low>=0)
            {
                if (x_low >= 0)
                {
                    v1 = input_src_device + dy * src_volume + src_w * y_low * 3 + x_low * 3;
                }
                if (x_high < src_w)
                {
                    v2 = input_src_device + dy * src_volume + src_w * y_low * 3 + x_high * 3;
                }
            }
            // v3 v4 通过 y_high绑定。constrain之后 y_high->[1,src_y-1]
            if (y_high<src_h)
            {
                if (x_low >= 0)
                {
                    v3 = input_src_device + dy * src_volume + src_w * y_high * 3 + x_low * 3;
                }
                if (x_high<src_w)
                {
                    v4 = input_src_device + dy * src_volume + src_w * y_high * 3 + x_high * 3;
                }

            }
            c0 = floorf(w1 * v1[0] + w2 * v2[0] + w3 * v3[0] + w4 * v4[0]);
            c1 = floorf(w1 * v1[1] + w2 * v2[1] + w3 * v3[1] + w4 * v4[1]);
            c2 = floorf(w1 * v1[2] + w2 * v2[2] + w3 * v3[2] + w4 * v4[2]);

        }
        float* pdst = resize_dst + dy * dst_volume + dst_w * dst_y * 3 + dst_x * 3 ;
        pdst[0] = c0;
        pdst[1] = c1;
        pdst[2] = c2;
        // pdst[0] = c2;
        // pdst[1] = c1;
        // pdst[2] = c0;
        
    }

}

void launch_cuda_resize_padding(tools::Param param, unsigned char* intput_src_device,float* resize_dst,const tools::AffineMatrix dst2src)
{
    dim3 block_size(param.BLOCK_SIZE,param.BLOCK_SIZE);
    dim3 grid_size((param.dst_w*param.dst_h + param.BLOCK_SIZE -1)/param.BLOCK_SIZE,
        (param.BLOCK_SIZE + param.BLOCK_SIZE -1)/param.BLOCK_SIZE);
    int batch_size = param.batch;
    float pad_value = param.pad_value;
    int src_w = param.src_w;
    int src_h = param.src_h;
    int src_volume = src_w * src_h * 3;
    int dst_w = param.dst_w;
    int dst_h = param.dst_h;
    int dst_volume = dst_w * dst_h * 3;
    int dst_area = dst_w * dst_h;
    resize_padding_device_kernel<<<grid_size,block_size, 0, nullptr>>>(intput_src_device,src_volume,src_w,src_h,resize_dst,dst_volume,dst_area,dst_w,dst_h,dst2src,batch_size,pad_value);

}







